#pragma once
#include <grpcpp/grpcpp.h>
#include "message.grpc.pb.h"
#include "const.h"
#include "Singleton.h"
#include "ConfigMgr.h"
using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;

using message::GetVarifyReq;
using message::GetVarifyRsp;
using message::VarifyService;

class RPConPool {
public:
	RPConPool(std::size_t poolSize, std::string host, std::string port) :poolSize_(poolSize), host_(host),
		port_(port), b_stop_(false){
		for (int i = 0; i < poolSize; i++) {
			std::shared_ptr<Channel> channel = grpc::CreateChannel(host + ":" + port, grpc::InsecureChannelCredentials());
			connections_.push(VarifyService::NewStub(channel));
		}
	}
	~RPConPool() {
		std::lock_guard<std::mutex> lock(mutex_);
		Close();
		while (!connections_.empty()) {
			connections_.pop();
		}
	};

	std::unique_ptr<VarifyService::Stub> getconnection() {
		std::unique_lock<std::mutex> lock(mutex_);
		cond_.wait(lock, [this]() {
			if (b_stop_) {
				return true;
			}
			return !connections_.empty();
			}
		);
		if (b_stop_) {
			return nullptr;
		}
		auto connection = std::move(connections_.front());
		connections_.pop();
		return connection;
	}
	void Close() {
		b_stop_ = true;
		cond_.notify_all();
	}

	void returnConnection(std::unique_ptr<VarifyService::Stub> connection) {
		std::lock_guard<std::mutex> lock(mutex_);
		if (b_stop_) {
			return;
		}
		connections_.push(std::move(connection));
		cond_.notify_one();
	}

	
private:
	std::atomic<bool> b_stop_;
	size_t poolSize_;
	std::string host_;
	std::string port_;
	std::queue<std::unique_ptr<VarifyService::Stub>> connections_;
	std::mutex mutex_;
	std::condition_variable cond_;
};


class VarifyGrpcClient:public Singleton<VarifyGrpcClient>
{
	friend Singleton<VarifyGrpcClient>;
public:
	GetVarifyRsp GetVarifyCode(std::string email) {
		ClientContext context;
		GetVarifyRsp reply;
		GetVarifyReq request;

		request.set_email(email);
		auto stub = pool_->getconnection();
		Status status= stub->GetVarifyCode(&context, request, &reply);
		if (status.ok()) {
			pool_->returnConnection(std::move(stub));
			return reply;
		}
		else {
			reply.set_error(ErrorCodes::RPCFailed);
			pool_->returnConnection(std::move(stub));
			return reply;
		}
		
	}
private:
	VarifyGrpcClient() {
		auto gCfgMgr =ConfigMgr::Inst();
		std::string host = gCfgMgr["VarifyServer"]["Host"];
		std::string port = gCfgMgr["VarifyServer"]["Port"];
		pool_.reset(new RPConPool(5, host, port));
	};
	std::unique_ptr<RPConPool> pool_;

};

